package beautiful.butterfly.server.httpserver.mvc.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassSearcher {
    private static Logger logger = LoggerFactory.getLogger(ClassSearcher.class);

    private static List<String> getClassFilePathList(String basePackage) {
        List<String> classFilePathList = new LinkedList<String>();
        String basePackagePath = basePackage.replace(".", "/");
        Enumeration<URL> enumeration = null;
        try {
            enumeration = ClassSearcher.class.getClassLoader().getResources(basePackagePath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        while (enumeration.hasMoreElements()) {
            URL url = enumeration.nextElement();
            if (url.getPath().contains(".jar")) {
                System.out.println("当前资源位于jar中:" + url.getPath());
                classFilePathList = findClassFilePathListInJar(basePackage, url);
            } else {
                System.out.println("当前资源位于classpath中:" + url.getPath());
                String baseDirectoryPath = url.getPath();
                if (basePackage != null && !"".equals(basePackage)) {
                    basePackagePath = "/" + basePackagePath;
                }
                classFilePathList = findClassFilePathListNotInJar(baseDirectoryPath, baseDirectoryPath.replace(basePackagePath, ""), ".class");
            }
        }
        return classFilePathList;

    }


    private static List<String> findClassFilePathListInJar(String basePackage, URL url) {
        List<String> list = new ArrayList<String>();
        try {
            JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
            JarFile jarFile = jarURLConnection.getJarFile();
            Enumeration<JarEntry> jarEntries = jarFile.entries();
            while (jarEntries.hasMoreElements()) {
                JarEntry jarEntry = jarEntries.nextElement();
                String jarEntryName = jarEntry
                        .getName(); // 类似：sun/security/internal/interfaces/TlsMasterSecret.class
                String clazzName = jarEntryName.replace("/", ".");
                if (clazzName.startsWith(basePackage) && jarEntryName.endsWith(".class")) {
                    list.add(clazzName.replace(".class", ""));
                }

            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return list;
    }

    private static List<String> findClassFilePathListNotInJar(String baseDirectoryPath,
                                                              String rootPackagePath, String targetFileName) {
        /**
         * 算法简述： 从某个给定的需查找的文件夹出发，搜索该文件夹的所有子文件夹及文件，
         * 若为文件，则进行匹配，匹配成功则加入结果集，若为子文件夹，则进队列。 队列不空，重复上述操作，队列为空，程序结束，返回结果。
         */
        if (rootPackagePath == null) {
            rootPackagePath = baseDirectoryPath;
        }
        List<String> classFilePathList = new ArrayList<String>();
        String subFileName = null;
        File baseDirectory = new File(baseDirectoryPath);
        if (!baseDirectory.exists() || !baseDirectory.isDirectory()) {
            logger.debug("查找错误:" + baseDirectoryPath + "不是一个目录!");
            throw new RuntimeException(baseDirectoryPath);
        } else {
            File[] filelist = baseDirectory.listFiles();
            int length = filelist.length;
            for (int i = 0; i < length; i++) {
                File subFile = filelist[i];
                if (!subFile.isDirectory()) {
                    subFileName = subFile.getName();
                    if (subFileName.endsWith(targetFileName)) {
                        String classname;
                        classname = subFile.toURI().getPath()
                                .replaceAll("^" + rootPackagePath + "(.*)\\.class", "$1");
                        if (classname.startsWith("/")) {
                            classname = classname.substring(classname.indexOf("/") + 1);
                        }
                        classname = classname.replaceAll("\\\\", "/").replaceAll("/", ".");
                        classFilePathList.add(classname);
                    }
                } else if (subFile.isDirectory()) {
                    // 文件夹
                    classFilePathList.addAll(findClassFilePathListNotInJar(filelist[i].getAbsolutePath(), rootPackagePath, targetFileName));
                }
            }
        }
        return classFilePathList;
    }
    //上面是扫码路径，核心方法

    /**
     * 在ClassPath下面查找指定类的子类
     */
    public static List<Class> findSubClass(Class superclass, String basePackage) {
        List<Class> classList = new ArrayList<Class>();
        for (String classFile : getClassFilePathList(basePackage)) {
            try {
                Class<?> classInFile = Class.forName(classFile);
                if (classInFile.getSuperclass() == superclass) {
                    classList.add(classInFile);
                }
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        return classList;
    }


    /**
     * 在ClassPath下面查找指定Annotation
     */

    public static List<Class> findAnnotationClassInClasspath(Class<? extends Annotation> annotationClass, String basePackage) {
        List<Class> classList = new ArrayList<Class>();
        for (String classFile : getClassFilePathList(basePackage)) {
            try {
                Class<?> classInFile = Class.forName(classFile);
                if (classInFile.isAnnotationPresent(annotationClass)) {
                    classList.add(classInFile);
                }
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        return classList;
    }


    /**
     * 在ClassPath下面查找含有目标接口的一级实现类
     */
    public static List<Class<?>> findImplementsClassInClassList(Class<?> clazz, String basePackage) {
        List<Class<?>> classList = new ArrayList<Class<?>>();
        for (String classFile : getClassFilePathList(basePackage)) {

            try {
                Class<?> classInFile = Class.forName(classFile);
                Class<?>[] interfaces = classInFile.getInterfaces();
                if (interfaces != null && interfaces.length > 0) {
                    int length = interfaces.length;
                    for (int i = 0; i < length; i++) {
                        if (interfaces[i].getName().equals(clazz.getName())) {
                            classList.add(classInFile);
                            break;
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        return classList;
    }


}